﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Net.Browser;
using System.ComponentModel;


namespace SimpleMultipleFileUploader
{
    public class FileUploader
    {

        #region Member Variables

        /// <summary>
        /// The web client used to asynchronously upload data.
        /// </summary>
        private WebClient webClient = new WebClient();

        /// <summary>
        /// Acts as an index in the file array and is used by multiple event
        /// handlers. 
        /// 
        /// Upload process starts with currentFileIndex = 0, and  when the 
        /// first file is successfully uploaded, the index is incremented. 
        /// 
        /// When the index is gets above the length of the array, then it 
        /// is evident that all the files have been uploaded. 
        /// 
        /// </summary>
        private int currentFileIndex;

        #endregion
        

        #region Business Logic

        
        /// <summary>
        /// This method should only be used for uploading single files.
        /// </summary>
        /// <param name="fileInfo">The file to upload</param>
        /// <param name="projectName">The server side folder where to upload the file.</param>
        public void UploadFile(FileInfo fileInfo, string projectName)
        {
            UploadFiles(new FileInfo[] { fileInfo }, projectName);
        }

        public void UploadFiles(FileInfo[] fileInfo, string folderName)
        {

            //ToDo: Throw an error when fileInfo is null.
            if (fileInfo == null)
                return;

            currentFileIndex = 0;

            //Find the first non-null file info.
            while (currentFileIndex < fileInfo.Length && fileInfo[currentFileIndex] == null)
                currentFileIndex++;

            //If all files were null, exit the method.
            if (currentFileIndex == fileInfo.Length)
            {
                //ToDo: might need an exception here. On the other, silent exit is not
                //such a bad thing, since if all files are null then basically without
                //uploading any files, the method is done.
                return;
            }

            //Start file upload from that first non-null file.
            Uri uri = GetUri(fileInfo[currentFileIndex], folderName);

            
            webClient.OpenWriteCompleted += (sender, e) =>
            {

                //Make sure that the file to be uploaded is not null.
                if (fileInfo[currentFileIndex] != null)
                {
                    //Create a file upload argument to pass to events.
                    FileUploadArgs fileUploadArgs = new FileUploadArgs() { FileName = fileInfo[currentFileIndex].Name };

                    OnFileUpladStarted(fileUploadArgs);
                    
                    //Open a file stream corresponding to the 
                    Stream fileStream = fileInfo[currentFileIndex].OpenRead();
                    PushData(fileStream, e.Result, fileUploadArgs);

                    //Close the streams.
                    e.Result.Close();
                    fileStream.Close();

                   
                }

            };

            webClient.WriteStreamClosed += (sender, e) =>
            {
                //ToDo: Output a helpful error.
                if (e.Error == null)
                {
                    //Create a file upload argument to pass to events.
                    FileUploadArgs fileUploadArgs = new FileUploadArgs() { FileName = fileInfo[currentFileIndex].Name };

                    OnFileUploadCompleted(fileUploadArgs);

                    currentFileIndex++;

                    //Try to find the next null object.
                    while (currentFileIndex < fileInfo.Length && fileInfo[currentFileIndex] == null)
                        currentFileIndex++;

                    //Check to see if there are more files waiting to be uploaded.
                    if (currentFileIndex < fileInfo.Length)
                    {
                        Uri nextUri = GetUri(fileInfo[currentFileIndex], folderName);

                        //Start another upload.
                        webClient.OpenWriteAsync(nextUri);

                    }

                    //All file uploads are complete.
                    else
                    {
                        OnAllFilesUploadCompleted(new FileUploadArgs());
                    }
                }

            };


            webClient.OpenWriteAsync(uri);
        }

        #endregion


        #region Helper Methods

        private static Uri GetUri(FileInfo fileInfo, string folderName)
        {
            
            #if (DEBUG)
                UriBuilder uriBuilder = new UriBuilder("http://localhost:38742/ImportDataReceiver.ashx");
            #else
                UriBuilder uriBuilder = new UriBuilder("http://your-domain-name:port-number/ImportDataReceiver.ashx");
            #endif


            uriBuilder.Query = string.Format("filename={0}&foldername={1}", fileInfo.Name, folderName);

            return uriBuilder.Uri;

        }

        private void PushData(Stream input, Stream output, FileUploadArgs fileUploadArgs)
        {
            byte[] buffer = new byte[4096];
            int bytesRead;

            int totalRead = 0;

            while ((bytesRead = input.Read(buffer, 0, buffer.Length)) != 0)
            {
                output.Write(buffer, 0, bytesRead);

                //Increment the bytes read.
                totalRead += bytesRead;

                //Update the progress of this upload.
                fileUploadArgs.Progress = (double)totalRead / (double)input.Length;
                
                OnFileUploadProgressChanged(fileUploadArgs);

            }
        }

        #endregion


        #region Events and Handlers
        
        public event EventHandler AllFilesUploadCompleted;

        public event EventHandler FileUploadCompleted;

        public event EventHandler FileUploadStarted;

        public event EventHandler FileUploadProgressChanged;


        private void OnFileUpladStarted(FileUploadArgs e)
        {
            EventHandler fileUploadStarted = FileUploadStarted;

            if (fileUploadStarted != null)
                fileUploadStarted(this, e);
            
        }

        private void OnFileUploadProgressChanged(FileUploadArgs e)
        {
            EventHandler fileUploadProgressChanged = FileUploadProgressChanged;

            if (fileUploadProgressChanged != null)
                fileUploadProgressChanged(this, e);
        }

        private void OnFileUploadCompleted(FileUploadArgs e)
        {
            EventHandler fileUploadCompleted = FileUploadCompleted;

            if (fileUploadCompleted != null)
                fileUploadCompleted(this, e);
        }

        private void OnAllFilesUploadCompleted(FileUploadArgs e)
        {
            EventHandler allFilesUploadCompleted = AllFilesUploadCompleted;

            if(allFilesUploadCompleted != null)
                allFilesUploadCompleted(this, e);

        }

        #endregion
    }
}
